/**
 * CleanRip - gc_dvd.c (originally from Cube64/Wii64)
 * Copyright (C) 2007, 2008, 2009, 2010, 2011 emu_kidid
 *
 * DVD Reading support for GC
 *
 * CleanRip homepage: http://code.google.com/p/cleanrip
 * email address: emukidid@gmail.com
 *
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 **/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ogc/dvd.h>
#include <malloc.h>
#include <string.h>
#include <gccore.h>
#include <unistd.h>
#include "gc_dvd.h"
#include "main.h"


/* DVD Stuff */
volatile unsigned long* dvd = (volatile unsigned long*)0xCC006000;

int init_dvd() {
	// Gamecube Mode
	DVD_Init();
	DVD_Reset(DVD_RESETHARD);
	dvd_read_id();

	if (!dvd_get_error()) {
		return 0; //we're ok
	}
	return -1;
}

int dvd_read_id() {
	dvd[0] = 0x2E;
	dvd[1] = 0;
	dvd[2] = 0xA8000040;
	dvd[3] = 0;
	dvd[4] = 0x20;
	dvd[5] = 0x80000000;
	dvd[6] = 0x20;
	dvd[7] = 3; // enable reading!
	while (dvd[7] & 1);
	if (dvd[0] & 0x4)
		return 1;
	return 0;
}

unsigned int dvd_get_error(void) {
	dvd[0] = 0x2E;
	dvd[1] = 0;
	dvd[2] = 0xe0000000;
	dvd[3] = 0;
	dvd[4] = 0;
	dvd[5] = 0;
	dvd[6] = 0;
	dvd[7] = 1; // IMM
	while (dvd[7] & 1);
	return dvd[8];
}

void dvd_motor_off() {
	dvd[0] = 0x2E;
	dvd[1] = 0;
	dvd[2] = 0xe3000000;
	dvd[3] = 0;
	dvd[4] = 0;
	dvd[5] = 0;
	dvd[6] = 0;
	dvd[7] = 1; // IMM
	while (dvd[7] & 1);
}

int dvd_read(void* dst, unsigned int len, u32 offset) {
	dvd[0] = 0x2E;
	dvd[1] = 0;
	dvd[2] = 0xA8000000;
	dvd[3] = offset >> 2;
	dvd[4] = len;
	dvd[5] = (unsigned long) dst;
	dvd[6] = len;
	dvd[7] = 3; // enable reading!
	DCInvalidateRange(dst, len);
	while (dvd[7] & 1);

	if (dvd[0] & 0x4)
		return 1;
	return 0;
}

static char error_str[256];
char *dvd_error_str() {
	unsigned int err = dvd_get_error();
	if (!err)
		return "OK";

	memset(&error_str[0], 0, 256);
	switch (err >> 24) {
	case 0:
		break;
	case 1:
		strcpy(&error_str[0], "Lid open");
		break;
	case 2:
		strcpy(&error_str[0], "No disk/Disk changed");
		break;
	case 3:
		strcpy(&error_str[0], "No disk");
		break;
	case 4:
		strcpy(&error_str[0], "Motor off");
		break;
	case 5:
		strcpy(&error_str[0], "Disk not initialized");
		break;
	}
	switch (err & 0xFFFFFF) {
	case 0:
		break;
	case 0x020400:
		strcat(&error_str[0], " Motor Stopped");
		break;
	case 0x020401:
		strcat(&error_str[0], " Disk ID not read");
		break;
	case 0x023A00:
		strcat(&error_str[0], " Medium not present / Cover opened");
		break;
	case 0x030200:
		strcat(&error_str[0], " No Seek complete");
		break;
	case 0x031100:
		strcat(&error_str[0], " UnRecoverd read error");
		break;
	case 0x040800:
		strcat(&error_str[0], " Transfer protocol error");
		break;
	case 0x052000:
		strcat(&error_str[0], " Invalid command operation code");
		break;
	case 0x052001:
		strcat(&error_str[0], " Audio Buffer not set");
		break;
	case 0x052100:
		strcat(&error_str[0], " Logical block address out of range");
		break;
	case 0x052400:
		strcat(&error_str[0], " Invalid Field in command packet");
		break;
	case 0x052401:
		strcat(&error_str[0], " Invalid audio command");
		break;
	case 0x052402:
		strcat(&error_str[0], " Configuration out of permitted period");
		break;
	case 0x056300:
		strcat(&error_str[0], " End of user area encountered on this track");
		break;
	case 0x062800:
		strcat(&error_str[0], " Medium may have changed");
		break;
	case 0x0B5A01:
		strcat(&error_str[0], " Operator medium removal request");
		break;
	}
	if (!error_str[0])
		sprintf(&error_str[0], "Unknown error %08X", err);
	return &error_str[0];

}

